using System;
using System.Collections.Generic;
using System.Text;
using System.Resources;
using System.Web.Compilation;
using System.Reflection;
using System.Globalization;
using System.Collections;
using System.Xml;
using System.IO;
using System.Linq;
using System.Web.Caching;

namespace NGroup.VTL.Core.Sample
{
    public class UpdatableResXResourceProviderFactory : ResourceProviderFactory
    {
        public override IResourceProvider CreateGlobalResourceProvider(string classKey)
        {
            return new GlobalResXResourceProvider(classKey);
        }

        public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
        {
            /*
             * Optimization: restrict file-based provider to sections known to use ASP.NET localization.
             * */
            if (virtualPath.ContainsIgnoreCase("/Pages/") || virtualPath.ContainsIgnoreCase("/Controls/"))
                return new LocalResXResourceProvider(virtualPath);

            /*
             * Return the basic (empty) provider for all other cases.
             * */
            return new BasicResXResourceProvider(new BasicResXResourceReader(new Dictionary<string, object>()));
        }
    }

    /// <summary>
    /// Base abstract class for updatable *.resx provider.
    /// </summary>
    abstract class UpdatableResXResourceProvider : IResourceProvider
    {
        /// <summary>
        /// Specified the default culture. 
        /// </summary>
        private static readonly CultureInfo DefaultUICulture = CultureInfo.GetCultureInfoByIetfLanguageTag("en-CA");

        /// <summary>
        /// Path offset where *.resx files are stored. Clearing this value will result in *.resx
        /// lookups in standard ASP.NET locations.
        /// </summary>
        protected static string DefaultStore = @"Resource";

        /// <summary>
        /// </summary>
        /// <param name="resourceKey"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object GetObject(string resourceKey, CultureInfo culture)
        {
            if (culture == null)
                culture = CultureInfo.CurrentUICulture;

            /* 
             * Optimization: avoid lookups for the DefaultUICulture and the InstalledUICulture.
             * */
            if (culture.Equals(DefaultUICulture) || culture.Equals(CultureInfo.InstalledUICulture))
                culture = null;

            BasicResXResourceReader reader = GetResourceCache(culture) as BasicResXResourceReader;

            if (reader != null && reader.Resources.Contains(resourceKey))
                return reader.Resources[resourceKey];
            else
                foreach (DictionaryEntry entry in reader)
                    if (resourceKey.Equals(entry.Key as string, StringComparison.OrdinalIgnoreCase))
                        return entry.Value;

            /*
             * If key wasn't found for the culture specified, retry the default culture.
             * */
            if (culture != null)
                return GetObject(resourceKey, DefaultUICulture);

            return null;
        }

        /// <summary>
        /// Relative path to *.resx file.
        /// </summary>
        public abstract string Path { get; }

        public IResourceReader ResourceReader
        {
            get
            {
                return GetResourceCache(null);
            }
        }

        private IResourceReader GetResourceCache(CultureInfo culture)
        {
            Cache cache = System.Web.HttpContext.Current.Cache;
            IResourceReader resourceReader;
            string fullPath = Resolve(Path, culture), key = "ResourceFile " + fullPath;

            if (null == (resourceReader = cache[key] as IResourceReader))
            {
                bool exists = File.Exists(fullPath);
                cache.Insert(key,
                             resourceReader = exists ? new BasicResXResourceReader(new ResXResourceReader(fullPath)) : new BasicResXResourceReader(new Dictionary<string, object>()),
                             exists ? new CacheDependency(fullPath) : null,
                             Cache.NoAbsoluteExpiration,
                             Cache.NoSlidingExpiration,
                             CacheItemPriority.NotRemovable,
                             new CacheItemRemovedCallback(this.OnRemoved));
            }

            return resourceReader;
        }

        /// <summary>
        /// CacheItemRemovedCallback to properly dispose of IDisposables.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="reason"></param>
        public void OnRemoved(string key, object value, CacheItemRemovedReason reason)
        {
            IDisposable disposable = value as IDisposable;

            if (null != disposable)
                disposable.Dispose();

            value = null;
        }

        /// <summary>
        /// Returns the absolute path to the *.resx corresponding to the request, culture requested.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        private static string Resolve(string path,
                                      CultureInfo culture)
        {
            if (null != culture)
                path = path.Replace(System.IO.Path.GetFileName(path),
                                    string.Format("{0}.{1}{2}",
                                                  System.IO.Path.GetFileNameWithoutExtension(path),
                                                  culture.Name,
                                                  System.IO.Path.GetExtension(path)));

            return System.Web.HttpContext.Current.Server.MapPath(path);
        }
    }

    /// <summary>
    /// Basic implementation of IResourceProvider using the specified BasicResXResourceReader.
    /// </summary>
    class BasicResXResourceProvider : IResourceProvider, IDisposable
    {
        private BasicResXResourceReader reader;

        public BasicResXResourceProvider(BasicResXResourceReader reader)
        {
            this.reader = reader;
        }

        public IResourceReader ResourceReader
        {
            get
            {
                return reader;
            }
        }

        public object GetObject(string resourceKey, CultureInfo culture)
        {
            foreach (DictionaryEntry entry in ResourceReader)
                if (resourceKey.Equals(entry.Key))
                    return entry.Value;

            return null;
        }

        public void Dispose()
        {
            if (null != reader)
                reader.Dispose();

            reader = null;
            GC.SuppressFinalize(this);
        }
    }

    /// <summary>
    /// Updatable *.resx provider for global resources.
    /// </summary>
    class GlobalResXResourceProvider : UpdatableResXResourceProvider
    {
        private string classKey;

        internal GlobalResXResourceProvider(string classKey)
        {
            this.classKey = classKey;
        }

        public override string Path
        {
            get
            {
                return string.Format(@"~/{0}App_GlobalResources/{1}.resx",
                                     DefaultStore,
                                     classKey);
            }
        }
    }

    /// <summary>
    /// Updatable *.resx provider for local resources.
    /// </summary>
    class LocalResXResourceProvider : UpdatableResXResourceProvider
    {
        private string virtualPath;

        public LocalResXResourceProvider(string virtualPath)
        {
            string appPath = System.Web.HttpContext.Current.Request.ApplicationPath;

            if (appPath.Length > 1)
                virtualPath = virtualPath.Replace(appPath, string.Empty);

            this.virtualPath = virtualPath;
        }

        public override string Path
        {
            get
            {
                return string.Format("~/{0}{1}/App_LocalResources/{2}.resx",
                                     DefaultStore,
                                     System.IO.Path.GetDirectoryName(virtualPath),
                                     System.IO.Path.GetFileName(virtualPath));
            }
        }
    }

    /// <summary>
    /// Basic implementation of IResourceReader using the specified IDictionary.
    /// </summary>
    class BasicResXResourceReader : IResourceReader, IDisposable
    {
        private IDictionary resources;

        public BasicResXResourceReader(IDictionary resources)
        {
            this.resources = resources;
        }

        public BasicResXResourceReader(IResourceReader reader)
        {
            resources = new Dictionary<string, Object>();

            foreach (DictionaryEntry entry in reader)
                resources.Add(entry.Key, entry.Value);
        }

        public IDictionary Resources
        {
            get
            {
                return resources;
            }
        }

        IDictionaryEnumerator IResourceReader.GetEnumerator()
        {
            return resources.GetEnumerator();
        }

        void IResourceReader.Close()
        {

        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return resources.GetEnumerator();
        }

        public void Dispose()
        {
            resources = null;
            GC.SuppressFinalize(this);
        }
    }

    public static class StringUtilities
    {
        public static bool ContainsIgnoreCase(this string input, string pattern)
        {
            return input.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) >= 0;
        }
    }
}
